using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using QueryBuilder.Core.Query.Exceptions;
using QueryBuilder.Core.Query.Properties;
using QueryBuilder.Core.Query.Type;

namespace QueryBuilder.Core.Query.Presenters
{
    /// <summary>
    /// 
    /// </summary>
    public abstract class AbstractPresenter : IConstraintPresenter
    {
        /// <summary>
        /// 
        /// </summary>
        private const string AS = "AS";
        /// <summary>
        /// 
        /// </summary>
        private const string Between = "BETWEEN";
        /// <summary>
        /// 
        /// </summary>
        private const string IN = "IN";
        /// <summary>
        /// 
        /// </summary>
        private const string IS = "IS";
        /// <summary>
        /// 
        /// </summary>
        private const string NULL = "NULL";
        /// <summary>
        /// 
        /// </summary>
        public const string PossibleClosedQuoteChars = "`'\"]";
        /// <summary>
        /// 
        /// </summary>
        public const string PossibleQuoteChars = "`'\"[";

        /// <summary>
        /// 
        /// </summary>
        private PresentationMappings<Aggregate> aggregateMappings = new PresentationMappings<Aggregate>();
        /// <summary>
        /// 
        /// </summary>
        private string AVG = "AVG";
        /// <summary>
        /// 
        /// </summary>
        private PresentationMappings<IEnumerable<Constraint>> comparisonMappings = new PresentationMappings<IEnumerable<Constraint>>();
        /// <summary>
        /// 
        /// </summary>
        private PresentationMappings<Join> joinMappings = new PresentationMappings<Join>();
        /// <summary>
        /// 
        /// </summary>
        private string Minus = "-";

        /// <summary>
        /// 
        /// </summary>
        private StringBuilder result = new StringBuilder();
        /// <summary>
        /// 
        /// </summary>
        private PresentationMappings<object> typeMappings = new PresentationMappings<object>();
        /// <summary>
        /// 
        /// </summary>
        private TypeMappingsRegistry typeMappingsRegistry;

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractPresenter"/> class.
        /// </summary>
        /// <param name="typeMappingsRegistry">The type mappings registry.</param>
        protected AbstractPresenter(TypeMappingsRegistry typeMappingsRegistry)
        {
            this.typeMappingsRegistry = typeMappingsRegistry;
            this.OpenQuote = "\"";
            this.CloseQuote = "\"";

            this.comparisonMappings.Add(Comparisons.BetweenAnd, this.PresentBetweenAnd);
            this.comparisonMappings.Add(Comparisons.Blank, this.PresentBlank);
            this.comparisonMappings.Add(Comparisons.CloseParentheses, this.PresentCloseParentheses);
            this.comparisonMappings.Add(Comparisons.EndsWith, this.PresentEndsWith);
            this.comparisonMappings.Add(Comparisons.Equals, this.PresentEquals);
            this.comparisonMappings.Add(Comparisons.GreaterOrEquals, this.PresentBetweenAnd);
            this.comparisonMappings.Add(Comparisons.GreaterThan, this.PresentGreaterThan);
            this.comparisonMappings.Add(Comparisons.In, this.PresentIn);
            this.comparisonMappings.Add(Comparisons.Is, this.PresentIs);
            this.comparisonMappings.Add(Comparisons.IsNot, this.PresentIsNot);
            this.comparisonMappings.Add(Comparisons.LessOrEquals, this.PresentLessOrEquals);
            this.comparisonMappings.Add(Comparisons.LessThan, this.PresentLessThan);
            this.comparisonMappings.Add(Comparisons.Like, this.PresentLike);
            this.ComparisonMappings.Add(this.Minus, this.PresentMinus);

            this.comparisonMappings.Add(Comparisons.NotEquals, this.PresentNotEquals);
            this.comparisonMappings.Add(Comparisons.NotIn, this.PresentNotIn);
            this.comparisonMappings.Add(Comparisons.NotLike, this.PresentNotLike);
            this.comparisonMappings.Add(Comparisons.OpenParentheses, this.PresentOpenParentheses);
            this.comparisonMappings.Add(Comparisons.StartsWith, this.PresentStartsWith);

            this.comparisonMappings.Add(Comparisons.OR, this.PresentOr);
            this.comparisonMappings.Add(Comparisons.Plus, this.PresentPlus);

            this.joinMappings.Add(JoinType.Cross, this.PresentCrossTypeJoin);
            this.joinMappings.Add(JoinType.Inner, this.PresentInnerTypeJoin);
            this.joinMappings.Add(JoinType.LeftInner, this.PresentLeftInnerTypeJoin);
            this.joinMappings.Add(JoinType.LeftOuter, this.PresentLeftOuterTypeJoin);
            this.joinMappings.Add(JoinType.NotEqual, this.PresentNotEqualTypeJoin);
            this.joinMappings.Add(JoinType.Outer, this.PresentOuterTypeJoin);
            this.joinMappings.Add(JoinType.RightInner, this.PresentRightInnerTypeJoin);
            this.joinMappings.Add(JoinType.RightOuter, this.PresentRightOuterTypeJoin);

            this.aggregateMappings.Add(AggregateFunctions.Count, this.PresentCountAggrigate);
            this.aggregateMappings.Add(AggregateFunctions.Avg, this.PresentAvgAggrigate);

            this.typeMappings.Add("String", this.PresentStringVarialbe);
            this.typeMappings.Add("Int32", this.PresentInt32Variable);
            this.typeMappings.Add("DateTime", this.PresentDateTimeVariable);
            this.typeMappings.Add("Boolean", this.PresentBooleanVariable);
            this.TypeMappings.Add("Double", this.PresentDoubleVariable);
        }

        /// <summary>
        /// Gets the join mappings.
        /// </summary>
        /// <value>The join mappings.</value>
        protected PresentationMappings<Join> JoinMappings
        {
            get { return this.joinMappings; }
        }

        /// <summary>
        /// Gets the comparison mappings.
        /// </summary>
        /// <value>The comparison mappings.</value>
        protected PresentationMappings<IEnumerable<Constraint>> ComparisonMappings
        {
            get { return this.comparisonMappings; }
        }

        /// <summary>
        /// Gets the aggregate mappings.
        /// </summary>
        /// <value>The aggregate mappings.</value>
        protected PresentationMappings<Aggregate> AggregateMappings
        {
            get { return this.aggregateMappings; }
        }

        /// <summary>
        /// Gets the type mappings.
        /// </summary>
        /// <value>The type mappings.</value>
        protected PresentationMappings<object> TypeMappings
        {
            get { return this.typeMappings; }
        }

        /// <summary>
        /// Gets or sets the open quote.
        /// </summary>
        /// <value>The open quote.</value>
        public virtual string OpenQuote { get; private set; }

        /// <summary>
        /// Gets or sets the close quote.
        /// </summary>
        /// <value>The close quote.</value>
        public virtual string CloseQuote { get; private set; }

        /// <summary>
        /// Gets or sets the result.
        /// </summary>
        /// <value>The result.</value>
        protected StringBuilder Result
        {
            get { return this.result; }

            set { this.result = value; }
        }

        /// <summary>
        /// Presents the specified variable.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="variable">The variable.</param>
        public virtual void Present<T>(Variable<T> variable)
        {
            ITypeMapping type = this.typeMappingsRegistry.GetTypeMapping(typeof (T).AssemblyQualifiedName);
            if (variable.Value == null)
            {
                this.PresentNullValue();
                return;
            }
            Action<object> factory = this.typeMappings.GetPresentationFactory(type.Name);
            if (factory != null)
            {
                factory.Invoke(variable.Value);
            }
        }

        /// <summary>
        /// Presents the specified column.
        /// </summary>
        /// <param name="column">The column.</param>
        public virtual void Present(Column column)
        {
            this.Present(column as IColumn);
        }

        /// <summary>
        /// Presents the specified aliased constraint.
        /// </summary>
        /// <param name="aliasedConstraint">The aliased constraint.</param>
        public virtual void Present(AliasedConstraint aliasedConstraint)
        {
            aliasedConstraint.Constraint.Accept(this);
            this.Result.Append(SqlFragments.Space);
            this.Result.Append(AS);
            this.Result.Append(SqlFragments.Space);
            this.Result.Append(aliasedConstraint.Alias);
        }

        public void Present(Condition condition)
        {
            Action<IEnumerable<Constraint>> factory = this.comparisonMappings.GetPresentationFactory(condition.Comparison);
            if (factory != null)
            {
                factory.Invoke(condition.Constraints);
                return;
            }

            throw new IllegalOperationException(string.Format(CultureInfo.CurrentCulture, Resources.UndefinedOperation,
                                                              condition.Comparison));
        }

        /// <summary>
        /// Presents the specified expression.
        /// </summary>
        /// <param name="expression">The expression.</param>
        public virtual void Present(Expression expression)
        {
            Action<IEnumerable<Constraint>> factory = this.comparisonMappings.GetPresentationFactory(expression.Comparison);
            if (factory != null)
            {
                factory.Invoke(expression.Constraints);
                return;
            }

            throw new IllegalOperationException(string.Format(CultureInfo.CurrentCulture, Resources.UndefinedOperation,
                                                              expression.Comparison));
        }

        /// <summary>
        /// Presents the specified variable.
        /// </summary>
        /// <param name="variable">The variable.</param>
        public virtual void Present(Variable variable)
        {
            if (variable.Type.Equals(typeof (string)))
            {
                this.Result.Append(this.OpenQuote);
                this.Result.Append(variable.Value);
                this.Result.Append(this.CloseQuote);
            }
        }

        /// <summary>
        /// Presents the specified aggregate.
        /// </summary>
        /// <param name="aggregate">The aggregate.</param>
        public virtual void Present(Aggregate aggregate)
        {
            Action<Aggregate> factory = this.aggregateMappings.GetPresentationFactory(aggregate.AggregateType);
            if (factory != null)
            {
                factory.Invoke(aggregate);
            }
        }

        /// <summary>
        /// Presents the avg aggrigate.
        /// </summary>
        /// <param name="aggregate">The aggregate.</param>
        protected virtual void PresentAvgAggrigate(Aggregate aggregate)
        {
            this.Result.Append(this.AVG);
            this.Result.Append(SqlFragments.OpenBracket);
            this.Present(aggregate.Column);
            this.Result.Append(SqlFragments.CloseBracket);
        }

        /// <summary>
        /// Presents the minus.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentMinus(IEnumerable<Constraint> constraints)
        {
            foreach (Constraint constraint in constraints)
            {
                constraint.Accept(this);

                if (constraint != constraints.Last())
                {
                    this.Result.Append(this.Minus);
                }
            }
        }

        /// <summary>
        /// Presents the double variable.
        /// </summary>
        /// <param name="variable">The variable.</param>
        protected virtual void PresentDoubleVariable(object variable)
        {
            var value = (double) variable;
            this.Result.Append((int) value);
            this.Result.Append(SqlFragments.Point);
            this.Result.Append((int) ((value - (int) (value*Math.Sign(value)))*100));
        }

        /// <summary>
        /// Presents the null value.
        /// </summary>
        protected virtual void PresentNullValue()
        {
            this.Result.Append(NULL);
        }

        /// <summary>
        /// Presents the specified column.
        /// </summary>
        /// <param name="column">The column.</param>
        public virtual void Present(IColumn column)
        {
            this.Result.Append(column.Table.Name);
            this.Result.Append(SqlFragments.Point);
            this.Result.Append(column.Name);
        }

        /// <summary>
        /// Presents the boolean variable.
        /// </summary>
        /// <param name="value">The value.</param>
        protected virtual void PresentBooleanVariable(object value)
        {
            this.Result.Append(value);
        }

        /// <summary>
        /// Presents the date time variable.
        /// </summary>
        /// <param name="value">The value.</param>
        protected virtual void PresentDateTimeVariable(object value)
        {
            var dateTime = (DateTime) value;
            this.Result.Append(dateTime.Year);
            this.Result.Append(SqlFragments.Worm);
            this.Result.Append(dateTime.Month);
            this.Result.Append(SqlFragments.Worm);
            this.Result.Append(dateTime.Day);
        }

        /// <summary>
        /// Presents the int32 variable.
        /// </summary>
        /// <param name="value">The value.</param>
        protected virtual void PresentInt32Variable(object value)
        {
            this.result.Append(value);
        }

        /// <summary>
        /// Presents the string varialbe.
        /// </summary>
        /// <param name="value">The value.</param>
        protected virtual void PresentStringVarialbe(object value)
        {
            this.Result.Append(this.OpenQuote);
            this.Result.Append(value);
            this.Result.Append(this.CloseQuote);
        }

        /// <summary>
        /// Converts to string.
        /// </summary>
        /// <param name="constraint">The constraint.</param>
        /// <returns></returns>
        public string ConvertToString(Constraint constraint)
        {
            this.Result = new StringBuilder();
            constraint.Accept(this);
            return this.Result.ToString();
        }

        /// <summary>
        /// Converts to string.
        /// </summary>
        /// <param name="join">The join.</param>
        /// <returns></returns>
        public string ConvertToString(Join join)
        {
            this.Result = new StringBuilder();
            this.PresentJoin(join);
            return this.Result.ToString();
        }

        /// <summary>
        /// Converts to string.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <returns></returns>
        public string ConvertToString(ITable table)
        {
            this.Result = new StringBuilder();
            this.PresentTable(table);
            return this.Result.ToString();
        }

        /// <summary>
        /// Converts to string.
        /// </summary>
        /// <param name="sqlQuery">The SQL query.</param>
        /// <returns></returns>
        public string ConvertToString(SqlQuery sqlQuery)
        {
            this.Result = new StringBuilder();
            this.PresentSqlQuery(sqlQuery);
            return this.Result.ToString();
        }


        /// <summary>
        /// Presents the starts with.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentStartsWith(IEnumerable<Constraint> constraints)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the plus.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentPlus(IEnumerable<Constraint> constraints)
        {
            if (constraints.Count() != 2)
            {
                throw new IllegalOperationException(Resources.IllegalCountArguments);
            }
            this.Result.Append(SqlFragments.OpenBracket);
            constraints.ElementAt(0).Accept(this);
            this.Result.Append(SqlFragments.Plus);
            constraints.ElementAt(1).Accept(this);
            this.Result.Append(SqlFragments.CloseBracket);
        }

        /// <summary>
        /// Presents the or.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentOr(IEnumerable<Constraint> constraints)
        {
            if (constraints.Count() != 2)
            {
                throw new IllegalOperationException(Resources.IllegalCountArguments);
            }
            this.Result.Append(SqlFragments.OpenBracket);
            constraints.ElementAt(0).Accept(this);
            this.Result.Append(SqlFragments.Space);
            this.Result.Append(SqlFragments.Or);
            this.Result.Append(SqlFragments.Space);
            constraints.ElementAt(1).Accept(this);
            this.Result.Append(SqlFragments.CloseBracket);
        }

        /// <summary>
        /// Presents the start with.
        /// </summary>
        /// <param name="expression">The expression.</param>
        protected virtual void PresentStartWith(Expression expression)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the open parentheses.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentOpenParentheses(IEnumerable<Constraint> constraints)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the not like.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentNotLike(IEnumerable<Constraint> constraints)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the not in.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentNotIn(IEnumerable<Constraint> constraints)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the not equals.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentNotEquals(IEnumerable<Constraint> constraints)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the like.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentLike(IEnumerable<Constraint> constraints)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the less than.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentLessThan(IEnumerable<Constraint> constraints)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the less or equals.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentLessOrEquals(IEnumerable<Constraint> constraints)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the is not.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentIsNot(IEnumerable<Constraint> constraints)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the is.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentIs(IEnumerable<Constraint> constraints)
        {
            if (constraints.Count() != 2)
            {
                throw new IllegalOperationException(string.Format(CultureInfo.CurrentCulture, Resources.IllegalCountArguments,
                                                                  SqlFragments.IS));
            }
            constraints.ElementAt(0).Accept(this);
            this.Result.Append(SqlFragments.Space);
            this.Result.Append(IS);
            this.Result.Append(SqlFragments.Space);
            constraints.ElementAt(1).Accept(this);
        }

        /// <summary>
        /// Presents the in.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentIn(IEnumerable<Constraint> constraints)
        {
            constraints.ElementAt(0).Accept(this);
            this.Result.Append(SqlFragments.Space);
            this.Result.Append(IN);
            this.Result.Append(SqlFragments.Space);
            this.Result.Append(SqlFragments.OpenBracket);
            constraints.ToList().ForEach(c =>
                                             {
                                                 if (c != constraints.First())
                                                 {
                                                     c.Accept(this);
                                                     if (c != constraints.Last())
                                                     {
                                                         this.Result.Append(SqlFragments.Comma);
                                                     }
                                                 }
                                             });
            this.Result.Append(SqlFragments.CloseBracket);
        }

        /// <summary>
        /// Presents the greater than.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentGreaterThan(IEnumerable<Constraint> constraints)
        {
            if (constraints.Count() != 2)
            {
                throw new IllegalOperationException(Resources.IllegalCountArguments);
            }

            this.Result.Append(SqlFragments.OpenBracket);
            constraints.ElementAt(0).Accept(this);
            this.Result.Append(SqlFragments.Greater);
            constraints.ElementAt(1).Accept(this);
            this.Result.Append(SqlFragments.CloseBracket);
        }

        /// <summary>
        /// Presents the greater or equals.
        /// </summary>
        /// <param name="expression">The expression.</param>
        protected virtual void PresentGreaterOrEquals(Expression expression)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the equals.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentEquals(IEnumerable<Constraint> constraints)
        {
            if (constraints.Count() != 2)
            {
                throw new IllegalOperationException(Resources.IllegalCountArguments);
            }

            this.Result.Append(SqlFragments.OpenBracket);
            constraints.ElementAt(0).Accept(this);
            this.Result.Append(SqlFragments.Equal);
            constraints.ElementAt(1).Accept(this);
            this.Result.Append(SqlFragments.CloseBracket);
        }

        /// <summary>
        /// Presents the ends with.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentEndsWith(IEnumerable<Constraint> constraints)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the close parentheses.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentCloseParentheses(IEnumerable<Constraint> constraints)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the blank.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentBlank(IEnumerable<Constraint> constraints)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the between and.
        /// </summary>
        /// <param name="constraints">The constraints.</param>
        protected virtual void PresentBetweenAnd(IEnumerable<Constraint> constraints)
        {
            if (constraints.Count() != 3)
            {
                throw new IllegalOperationException(string.Format(CultureInfo.CurrentCulture, Resources.IllegalCountArguments,
                                                                  SqlFragments.BETWEENAND));
            }
            constraints.ElementAt(0).Accept(this);
            this.Result.Append(SqlFragments.Space);
            constraints.ElementAt(1).Accept(this);
            this.Result.Append(SqlFragments.Space);
            this.Result.Append(Between);
            this.Result.Append(SqlFragments.Space);
            constraints.ElementAt(2).Accept(this);
        }

        /// <summary>
        /// Presents the SQL query.
        /// </summary>
        /// <param name="query">The query.</param>
        protected virtual void PresentSqlQuery(SqlQuery query)
        {
            this.Result.Append(SqlFragments.SELECT);
            this.Result.Append(SqlFragments.Space);
            if (query.IsSelectAllFields)
            {
                this.Result.Append(SqlFragments.Asterisk);
            }
            else
            {
                query.SelectSource.ToList().ForEach((c) =>
                                                        {
                                                            c.Accept(this);
                                                            if (c != query.SelectSource.Last())
                                                            {
                                                                this.Result.Append(SqlFragments.Comma);
                                                            }
                                                        });
            }


            if (query.FromTables.Count() > 0)
            {
                this.Result.Append(SqlFragments.Space);
                this.Result.Append(SqlFragments.FROM);
                this.Result.Append(SqlFragments.Space);
                query.FromTables.ToList().ForEach
                    (
                    (c) =>
                        {
                            this.PresentTable(c);
                            if (c != query.FromTables.Last())
                            {
                                this.Result.Append(SqlFragments.Comma);
                            }
                        }
                    );
            }


            if (query.Joins.Count() > 0)
            {
                this.Result.Append(SqlFragments.Space);
                this.Result.Append(SqlFragments.FROM);
                this.Result.Append(SqlFragments.Space);
            }
            query.Joins.ToList().ForEach(j =>
                                             {
                                                 this.PresentJoin(j);
                                                 if (j != query.Joins.Last())
                                                 {
                                                     this.Result.Append(SqlFragments.Comma);
                                                 }
                                             });

            if (query.Constraint != null)
            {
                this.Result.Append(SqlFragments.Space);
                this.Result.Append(SqlFragments.WHERE);
                this.Result.Append(SqlFragments.Space);
                query.Constraint.Accept(this);
            }

            if (query.GroupBys.Count() > 0)
            {
                this.Result.Append(SqlFragments.Space);
                this.Result.Append(SqlFragments.GROUPBY);
                this.Result.Append(SqlFragments.Space);
                foreach (Column groupBy in query.GroupBys)
                {
                    groupBy.Accept(this);
                    if (groupBy != query.GroupBys.Last())
                    {
                        this.Result.Append(SqlFragments.Comma);
                    }
                }
            }

            if (query.OrderBys.Count() > 0)
            {
                this.Result.Append(SqlFragments.Space);
                this.Result.Append(SqlFragments.ORDERBY);
                this.Result.Append(SqlFragments.Space);
                foreach (OrderBy orderBy in query.OrderBys)
                {
                    orderBy.Constraint.Accept(this);

                    switch (orderBy.Type)
                    {
                        case OrderByType.Asc:
                            this.Result.Append(SqlFragments.Space);
                            this.Result.Append(SqlFragments.ASC);
                            break;
                        case OrderByType.Desc:
                            this.Result.Append(SqlFragments.Space);
                            this.Result.Append(SqlFragments.DESC);
                            break;
                        case OrderByType.Null:
                            break;
                    }

                    if (orderBy != query.OrderBys.Last())
                    {
                        this.Result.Append(SqlFragments.Comma);
                    }
                }
            }
        }

        /// <summary>
        /// Presents the table.
        /// </summary>
        /// <param name="table">The table.</param>
        protected virtual void PresentTable(ITable table)
        {
            if (!string.IsNullOrEmpty(table.SchemaName))
            {
                this.Result.Append(table.SchemaName);
                this.Result.Append(SqlFragments.Point);
            }
            this.Result.Append(table.Name);
        }

        /// <summary>
        /// Presents the right outer type join.
        /// </summary>
        /// <param name="join">The join.</param>
        protected virtual void PresentRightOuterTypeJoin(Join join)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the right inner type join.
        /// </summary>
        /// <param name="join">The join.</param>
        protected virtual void PresentRightInnerTypeJoin(Join join)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the outer type join.
        /// </summary>
        /// <param name="join">The join.</param>
        protected virtual void PresentOuterTypeJoin(Join join)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the not equal type join.
        /// </summary>
        /// <param name="join">The join.</param>
        protected virtual void PresentNotEqualTypeJoin(Join join)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Presents the left outer type join.
        /// </summary>
        /// <param name="join">The join.</param>
        protected virtual void PresentLeftOuterTypeJoin(Join join)
        {
            this.PresentTable(join.FromTable);
            this.Result.Append(SqlFragments.Space);
            this.Result.Append(SqlFragments.LeftJoin);
            this.Result.Append(SqlFragments.Space);
            this.PresentTable(join.ToTable);
        }

        /// <summary>
        /// Presents the left inner type join.
        /// </summary>
        /// <param name="join">The join.</param>
        protected virtual void PresentLeftInnerTypeJoin(Join join)
        {
            this.PresentTable(join.FromTable);
            this.Result.Append(SqlFragments.Space);
            this.Result.Append(SqlFragments.LeftJoin);
            this.Result.Append(SqlFragments.Space);
            this.PresentTable(join.ToTable);
        }

        /// <summary>
        /// Presents the inner type join.
        /// </summary>
        /// <param name="join">The join.</param>
        protected virtual void PresentInnerTypeJoin(Join join)
        {
            this.PresentTable(join.FromTable);
            this.Result.Append(SqlFragments.Space);
            this.Result.Append(SqlFragments.INNERJOIN);
            this.Result.Append(SqlFragments.Space);
            this.PresentTable(join.ToTable);
        }

        /// <summary>
        /// Presents the cross type join.
        /// </summary>
        /// <param name="join">The join.</param>
        protected virtual void PresentCrossTypeJoin(Join join)
        {
            this.PresentTable(join.FromTable);
            this.Result.Append(SqlFragments.Space);
            this.Result.Append(SqlFragments.CorssJoin);
            this.Result.Append(SqlFragments.Space);
            this.PresentTable(join.ToTable);
        }

        /// <summary>
        /// Presents the join.
        /// </summary>
        /// <param name="join">The join.</param>
        protected virtual void PresentJoin(Join join)
        {
            Action<Join> factory = this.joinMappings.GetPresentationFactory(join.JoinType);
            if (factory != null)
            {
                factory.Invoke(join);

                if (join.Constraint != null)
                {
                    this.Result.Append(SqlFragments.Space);
                    this.Result.Append(SqlFragments.ON);
                    this.Result.Append(SqlFragments.Space);
                    join.Constraint.Accept(this);
                }
                else if (join.Usings.Count() > 0)
                {
                    this.Result.Append(SqlFragments.Space);
                    this.Result.Append(SqlFragments.Using);
                    this.Result.Append(SqlFragments.OpenBracket);

                    foreach (string joinUsing in join.Usings)
                    {
                        this.Result.Append(joinUsing);
                        if (joinUsing != join.Usings.Last())
                        {
                            this.Result.Append(SqlFragments.Comma);
                        }
                    }

                    this.Result.Append(SqlFragments.CloseBracket);
                }

                return;
            }

            throw new IllegalOperationException(string.Format(CultureInfo.CurrentCulture, Resources.UndefinedJoinType,
                                                              join.JoinType));
        }

        /// <summary>
        /// Presents the count aggrigate.
        /// </summary>
        /// <param name="aggregate">The aggregate.</param>
        protected virtual void PresentCountAggrigate(Aggregate aggregate)
        {
            this.Result.Append(SqlFragments.COUNT);
            this.Result.Append(SqlFragments.OpenBracket);
            if (aggregate.IsAllFields)
            {
                this.Result.Append(SqlFragments.Asterisk);
            }
            else
            {
                this.Present(aggregate.Column);
            }

            this.Result.Append(SqlFragments.CloseBracket);
        }
    }
}